----------Weather Forecasting----------
A 4am crack                  2016-08-22
---------------------------------------

Name: Weather Forecasting
Genre: educational
Year: 1986
Authors: Edunetics
Publisher: Prentice-Hall
Platform: Apple ][+ or later
Media: two double-sided 5.25-inch disks
OS: Diversi-DOS C1983
Previous cracks: none

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  no errors, but disk boots then
  displays a graphical error message
  "You are experiencing operating
  difficulty" and hangs

Locksmith Fast Disk Backup
  ditto

EDD 4 bit copy (no sync, no count)
  ditto

Copy ][+ nibble editor
  nothing suspicious
  nothing on track $23
  hi-res disk scan shows nothing
  out of the ordinary

Disk Fixer
  T00 -> looks like DOS 3.3-shaped RWTS
  T01-2 -> looks like DOS 3.3
  T02,S02 -> "C1983 DSR" (Diversi-DOS)
  T11 -> standard DOS 3.3 disk catalog,
  no funny stuff

Why didn't any of my copies work?
  probably a runtime protection check,
  since my copy has no bad sectors and
  the original works just fine

Next steps:

  1. Find the protection check
  2. Disable it
  3. I don't know, go feed the ducks or
     something?

                   ~

               Chapter 1
      In Which We Get Right To It


Turning to my trusty Disk Fixer sector
editor, I search for "BD 89 C0" (the
most common way to turn on the drive
motor manually -- "LDA $C089,X").
Mirabile dictu!

                 --v--

------------- DISK SEARCH -------------

$00/$07-$4F   $05/$03-$21


             PRESS [RETURN]

                 --^--

The hit on track $00 is part of the
legitimate RWTS. Track $05, on the
other hand...

                 --v--

T05,S03
----------- DISASSEMBLY MODE ----------
; turning on the drive motor manually
; is always suspicious
0021:BD 89 C0       LDA   $C089,X

; initialize death counters
0024:A9 56          LDA   #$56
0026:85 1C          STA   $1C
0028:A9 08          LDA   #$08
002A:C6 1B          DEC   $1B
002C:D0 04          BNE   $0032
002E:C6 1C          DEC   $1C

; if death counter hits 0, give up
0030:F0 3D          BEQ   $006F

; look for an $FB nibble
0032:BC 8C C0       LDY   $C08C,X
0035:10 FB          BPL   $0032
0037:C0 FB          CPY   #$FB
0039:D0 ED          BNE   $0028
003B:F0 00          BEQ   $003D

; kill a few cycles (not pointless,
; because the disk spins independently
; of the CPU, so all of these low-level
; disk reads are highly time-sensitive)
003D:EA             NOP
003E:EA             NOP

; read data latch (note: no BPL loop
; here, we're just reading it once)
003F:BC 8C C0       LDY   $C08C,X

; do a compare to set or clear the
; carry bit (among other things, but
; it's the carry bit we care about)
0042:C0 08          CPY   #$08

; rotate the carry into the low bit of
; the accumulator
0044:2A             ROL

; if we just rolled a "1" bit out of
; the high bit of the accumulator, take
; this branch
0045:B0 0B          BCS   $0052

; next nibble needs to be $FF
0047:BC 8C C0       LDY   $C08C,X
004A:10 FB          BPL   $0047

; ...otherwise we start over
004C:C0 FF          CPY   #$FF
004E:D0 D8          BNE   $0028

; loop back to get next nibble
0050:F0 EB          BEQ   $003D

; execution continues here (from offset
; $0045)
; get another nibble
0052:BC 8C C0       LDY   $C08C,X
0055:10 FB          BPL   $0052

; stash it in zero page
0057:84 1A          STY   $1A

; if the accumulator is anything but
; %00001010, start over
0059:C9 0A          CMP   #$0A
005B:D0 CB          BNE   $0028

                 --^--

I got lost several times trying to
follow this routine. I think the
easiest way to explain it is to show
the difference between the original
disk and my non-working copy.

Here is the original disk, as seen by
the Copy II+ nibble editor. Nibbles
with extra "0" bits (timing bits) after
them are displayed in inverse on an
original machine, marked here with a
"+" after the nibble.

                 --v--

   COPY ][ PLUS BIT COPY PROGRAM 8.4
(C) 1982-9 CENTRAL POINT SOFTWARE, INC.
---------------------------------------

TRACK:     START: 1B1E  LENGTH: 17C1

1C70: 9F EB E5 FC D7 D7 D7 EE   VIEW
1C78: FA E6 E6 FF FE F2 ED FD
1C80: FF EF ED BA BB DD AF E6
1C88: B7 A7 CB B7 DE AA EB FF
1C90: FF FF FF FB+FF FF+FF FF+
1C98: FD FF+FF+FF+FF+FF+FF+FF+
1CA0: FF+FF+D5 AA 96 AA AB AA
1CA8: AA AA AB AA AA AB FF FF+
1CB0: FF+FF+FF+FF+FF+FF D5 AA
---------------------------------------

  A  TO ANALYZE DATA  ESC TO QUIT

  ?  FOR HELP SCREEN  /  CHANGE PARMS

  Q  FOR NEXT TRACK   SPACE TO RE-READ

                 --^--

It's easy to understand why a simple
sector copy failed. The sequence that
this code is looking for starts at
offset $1C93, which is between the end
of one sector and the beginning of the
next. (The data epilogue is at $1C8C;
the next address prologue is at $1CA2.)
Sector copiers discard everything
between those delimiters and rebuild
the track with a default pattern of
sync bytes. That pattern doesn't
include an $FB nibble, so the nibble
check fails.

But the EDD bit copy also failed. Here
is the original disk's pattern at
offset $1C93:

  - $FB + timing bit
  - $FF
  - $FF + timing bit
  - $FF
  - $FF + timing bit

And here is what the same part of the
track looks like on my failed EDD copy:

                 --v--

   COPY ][ PLUS BIT COPY PROGRAM 8.4
(C) 1982-9 CENTRAL POINT SOFTWARE, INC.
---------------------------------------

TRACK:     START: 1B1E  LENGTH: 17C1

1C70: 9F EB E5 FC D7 D7 D7 EE   VIEW
1C78: FA E6 E6 FF FE F2 ED FD
1C80: FF EF ED BA BB DD AF E6
1C88: B7 A7 CB B7 DE AA EB FF
1C90: FF FF FF FB+FF FF FF+FF+
1C98: FD FF+FF+FF+FF+FF+FF+FF+
1CA0: FF+FF+D5 AA 96 AA AB AA
1CA8: AA AA AB AA AA AB FF FF+
1CB0: FF+FF+FF+FF+FF+FF D5 AA
---------------------------------------

  A  TO ANALYZE DATA  ESC TO QUIT

  ?  FOR HELP SCREEN  /  CHANGE PARMS

  Q  FOR NEXT TRACK   SPACE TO RE-READ

                 --^--

A subtle difference! The sequence at
offset $1C93 now looks like this:

  - $FB + timing bit
  - $FF
  - $FF
  - $FF + timing bit
  - $FF + timing bit

This code is looking for $FF bytes with
an alternating pattern of timing bit,
no timing bit, timing bit, no timing
bit. The accumulator holds the pattern
of whether each sync byte had a timing
bit after it. It's set one bit at a
time, rotated into place from the carry
bit that was set by the "CPY #$08" that
happened after getting the value of the
data latch (LDY $C08C,X) that happened
after doing just enough NOPs that the
value of the data latch will depend on
the presence of a timing bit after the
previous nibble.

Which is brilliant.

Anyway, if the value of the accumulator
(i.e. the pattern of timing bits) is
wrong, the program knows it's not
running on an original disk.

                   ~

               Chapter 2
 In Which It All Comes Down To One Bit


Continuing the listing of T05,S03...

                 --v--

; get a nibble
005D:BD 8C C0       LDA   $C08C,X
0060:10 FB          BPL   $005D

; more bit twiddling
0062:38             SEC
0063:2A             ROL

; AND it with the previously stashed
; nibble
0064:25 1A          AND   $1A
0066:49 FF          EOR   #$FF

; branch to failure path
0068:D0 05          BNE   $006F

; success path falls through to here --
; set Y to 1, turn off the drive motor,
; and return to the caller
006A:A0 01          LDY   #$01
006C:4C 71 4B       JMP   $4B71

; failure path is here (from offset
; $0030 or offset $0068) --
; set Y to 0, turn off the drive motor,
; and return to the caller
006F:A0 00          LDY   #$00
0071:DD 88 C0       CMP   $C088,X
0074:60             RTS

                 --^--

Aha! A solution presents itself!

The caller is sensitive to the value of
Y on exit. If I set Y to 1 at offset
$006F (the failure path), the caller
should be fooled into thinking that the
protection check passed.

T05,03,$70: 00 -> 01

]PR#6
...works...

Disks 2-4 are unprotected.

Quod erat liberandum.

---------------------------------------
A 4am crack                     No. 810
------------------EOF------------------
